﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using System.Diagnostics.Contracts;

namespace SimpleIR
{
	/// <summary>
	/// Abstract base type to represent all invocation signatures.
	/// </summary>
	public abstract class IRInvokeSig
	{
		private readonly String m_name;
		private readonly List<IRType> m_sigt;
		private readonly bool m_isStatic;

		[ContractInvariantMethod]
		private void ObjectInvariant()
		{ 
			Contract.Invariant(!String.IsNullOrEmpty(this.m_name));
			Contract.Invariant(this.m_sigt != null);
		}

		protected IRInvokeSig(String mn, List<IRType> st, bool isStat)
		{
			Contract.Requires(!String.IsNullOrEmpty(mn));
			Contract.Requires(st != null);
			
			this.m_name = mn;
			this.m_sigt = st;
			this.m_isStatic = isStat;
		}

		public String StringifyNameAndArgList()
		{
			Contract.Ensures(!String.IsNullOrEmpty(Contract.Result<String>()));

			StringBuilder sb = new StringBuilder(this.m_name + "(");
			for(int i = 0; i < this.m_sigt.Count; ++i)
			{
				if(i != 0)
					sb.Append(",");

				sb.Append(this.m_sigt[i].ToString());
			}
			sb.Append(")");
			return sb.ToString();
		}

		public String InvokeName 
		{ 
			get 
			{
				Contract.Ensures(!String.IsNullOrEmpty(Contract.Result<String>()));

				return this.m_name; 
			} 
		}
		
		public List<IRType> InvokeSig 
		{ 
			get 
			{
				Contract.Ensures(Contract.Result<List<IRType>>() != null && (!Contract.Result<List<IRType>>().Contains(null)));

				return this.m_sigt; 
			} 
		}

		public bool IsConstructor { get { return this.m_name.Equals(".ctor"); } }
		public bool IsVirtualInvoke { get { return !this.m_isStatic; } }
		public bool IsStaticInvoke { get { return this.m_isStatic; } }
	}


	/// <summary>
	/// Class which represents static invocation signatures.
	/// </summary>
	public sealed class IRStaticInvoke : IRInvokeSig
	{
		private readonly IRType m_defClass;

		[ContractInvariantMethod]
		private void ObjectInvariant()
		{ Contract.Invariant(this.m_defClass != null); }

		internal IRStaticInvoke(IRType defcl, String name, List<IRType> sigt)
			: base(name, sigt, true)
		{
			Contract.Requires(!String.IsNullOrEmpty(name));
			Contract.Requires(sigt != null && (!sigt.Contains(null)));
			Contract.Requires(defcl != null);

			this.m_defClass = defcl; 
		}

		public override String ToString()
		{ return this.m_defClass.ToString() + "::" + this.StringifyNameAndArgList(); }

		public IRType DefClass 
		{ 
			get 
			{
				Contract.Ensures(Contract.Result<IRType>() != null);

				return this.m_defClass; 
			} 
		}
	}

	public sealed class IRVirtualInvoke : IRInvokeSig
	{
		internal IRVirtualInvoke(String name, List<IRType> sigt)
			: base(name, sigt, false)
		{
			Contract.Requires(!String.IsNullOrEmpty(name));
			Contract.Requires(sigt != null && (!sigt.Contains(null)));
		}

		public override String ToString()
		{ return "virt@" + this.StringifyNameAndArgList(); }
	}

	/// <summary>
	/// Class which represents member invocation signatures.
	/// </summary>
	public sealed class IRInvokeIdentity
	{
		private readonly IRType m_defInClass;
		private readonly IRInvokeSig m_sig;

		private int m_invidstableorder;

		[ContractInvariantMethod]
		private void ObjectInvariant()
		{ 
			Contract.Invariant(this.m_defInClass != null);
			Contract.Invariant(this.m_sig != null);
		}

		internal IRInvokeIdentity(IRType tt, IRInvokeSig invs)
		{
			Contract.Requires(tt != null);
			Contract.Requires(invs != null);

			this.m_defInClass = tt;
			this.m_sig = invs;
			this.m_invidstableorder = -1;
		}

		public override String ToString()
		{
			if(this.m_sig.IsStaticInvoke)
				return this.m_sig.ToString();
			else
				return this.m_defInClass.ToString() + "::" + this.m_sig.ToString();
		}

		/// <summary>
		/// Compute and order on the invoke ids based on their string name order to ensure stable sorting later.
		/// </summary>
		internal static void ComputeInvokeIDStableOrder(List<IRInvokeIdentity> iidl)
		{
			iidl.Sort((IRInvokeIdentity iidl1, IRInvokeIdentity iidl2) => iidl1.ToString().CompareTo(iidl2.ToString()));

			for(int i = 0; i < iidl.Count; ++i)
				iidl[i].m_invidstableorder = i;
		}

		/// <summary>
		/// Order the invoke identifiers based on the stable ids (which are the lexo-sort order of the full names at load time).
		/// </summary>
		public static int ComputeInvokeCompareByName(IRInvokeIdentity ii1, IRInvokeIdentity ii2)
		{
			Contract.Assert(ii1 != null && ii1.m_invidstableorder != -1);
			Contract.Assert(ii2 != null && ii2.m_invidstableorder != -1);

			return (ii1.m_invidstableorder - ii2.m_invidstableorder);
		}

		public IRType DefInClass 
		{
			get 
			{
				Contract.Ensures(Contract.Result<IRType>() != null);

				return this.m_defInClass; 
			} 
		}
		
		public IRInvokeSig Sig 
		{ 
			get 
			{
				Contract.Ensures(Contract.Result<IRInvokeSig>() != null);

				return this.m_sig; 
			} 
		}
	}
}
